/*
 * @(#) $Header$
 *
 * Copyright (C)  2006  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package ca.forklabs.baselib.util;

import java.util.List;
import java.util.ListIterator;
import ca.forklabs.baselib.util.Resources;

/**
 * Class {@code Arrays} contains methods that acts on arrays. It complements the
 * core library class {@link java.util.Arrays}.
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.baselib.util.Arrays">Daniel Léonard</a>
 * @version $Revision$
 */
public class Arrays {

//---------------------------
// Constructor
//---------------------------

   /**
    * Let no one instanciate this class.
    */
   private Arrays() {
   // nothing
      }


//---------------------------
// Class methods
//---------------------------

   /**
    * Creates an iterator over the given array. This method performs the same
    * thing as {@code Iterators#asIterator(Object[])}
    * @param   <E>   the type of the elements in the array.
    * @param   array   the array.
    * @return   the iterator.
    */
   @SafeVarargs
   public static <E> ListIterator<E> iterator(E... array) {
      int index = 0;
      ListIterator<E> iter = Arrays.iterator(array, index);
      return iter;
      }

   /**
    * Creates an iterator over the given array. This method performs the same
    * thing as {@code Iterators#asIterator(Object[], int)}
    * @param   <E>   the type of the elements in the array.
    * @param   array   the array.
    * @param   index   the starting index.
    * @return   the iterator.
    */
   public static <E> ListIterator<E> iterator(E[] array, int index) {
      ListIterator<E> iter = Iterators.asIterator(array, index);
      return iter;
      }

   /**
    * Creates an {@link Iterable} over the given array. This method performs the
    * same thing as {@code Iterators#asIterable(Object...)}
    * @param   <E>   the type of the elements in the array.
    * @param   array   the array.
    * @return   the iterable.
    */
   @SafeVarargs
   public static <E> Iterable<E> iterable(E... array) {
      List<E> list = java.util.Arrays.asList(array);
      return list;
      }

   /**
    * Checks to see if the given object is {@code null}.
    * @param   object   the object
    * @throws   NullPointerException   if the object is {@code null}.
    */
   public static void checkNull(Object object) throws NullPointerException {
      if (null == object) {
         String message = Arrays.getObjectNullErrorMessage();
         throw new NullPointerException(message);
         }
      }

   /**
    * Given the array length, checks to see if the region
    * {@code [off, off + len]} is within the array bounds. The method
    * throws an exception if the region, or part of it, is not within the
    * bounds of the array.
    * @param  arrayLen  the length of the array.
    * @param  off       the offset in the the array.
    * @param  len       the length of the interval.
    * @exception  ArrayIndexOutOfBoundsException  if {@code arrayLen &lt; 0}.
    * @exception  ArrayIndexOutOfBoundsException  if {@code off} is not within }[0, arrayLen - 1].
    * @exception  IllegalArgumentException        if {@code len &lt; 0}.
    * @exception  IllegalArgumentException        if {@code off + len &gt; arrayLen}.
    */
   public static void checkOffLen(int arrayLen, int off, int len) throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
      if ((arrayLen == 0) && (off == 0) && (len == 0)) {
         return;
         }
      if (arrayLen < 0) {
         String message = Arrays.getNoValidIntervalErrorMessage(arrayLen);
         throw new ArrayIndexOutOfBoundsException(message);
         }
      if (off < 0 || off > arrayLen - 1) {
         String message = Arrays.getBadOffsetErrorMessage(off, arrayLen);
         throw new ArrayIndexOutOfBoundsException(message);
         }
      if (len < 0) {
         String message = Arrays.getBadLenghtErrorMessage(len);
         throw new IllegalArgumentException(message);
         }
      if ((off + len) > arrayLen) {
         String message = Arrays.getEndNotWithinBoundsErrorMessage(arrayLen, off, len);
         throw new IllegalArgumentException(message);
         }
      }


//---------------------------
// Error message class methods
//---------------------------

   /**
    * Gets the formatted error message that says that the object is
    * {@code null}.
    * @return   the formatted error message.
    */
   protected static String getObjectNullErrorMessage() {
      String key = Resources.ARRAYS_NULL;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   /**
    * Gets the formatted error message that says that there exist no interval.
    * @param   len   the lenght of the array.
    * @return   the formatted error message.
    */
   @SuppressWarnings("boxing")
   protected static String getNoValidIntervalErrorMessage(int len) {
      String key = Resources.ARRAYS_ZERO_LENGTH;
      String message = Resources.getLocalizedString(key, len);
      return message;
      }

   /**
    * Gets the formatted error message that says that the offset is out of
    * bounds.
    * @param   off   the offset.
    * @param   len   the len of the array.
    * @return   the formatted error message.
    */
   @SuppressWarnings("boxing")
   protected static String getBadOffsetErrorMessage(int off, int len) {
      String key = Resources.ARRAYS_NOT_WITHIN_BOUNDS;
      String message = Resources.getLocalizedString(key, off, len);
      return message;
      }

   /**
    * Gets the formatted error message that says that the length of the interval
    * is negative.
    * @param   len   the length of the interval.
    * @return   the formatted error message.
    */
   @SuppressWarnings("boxing")
   protected static String getBadLenghtErrorMessage(int len) {
      String key = Resources.ARRAYS_NEGATIVE_LENGTH;
      String message = Resources.getLocalizedString(key, len);
      return message;
      }

   /**
    * Gets the formatted error message that says that the end of the interval
    * goes past the end of the array.
    * @param   arrayLen   the length of the array.
    * @param   off   the offset in the array.
    * @param   len   the length of the interval.
    * @return   the formatted error message.
    */
   @SuppressWarnings("boxing")
   protected static String getEndNotWithinBoundsErrorMessage(int arrayLen, int off, int len) {
      String key = Resources.ARRAYS_END_NOT_WITHIN_BOUNDS;
      String message = Resources.getLocalizedString(key, arrayLen, off, len);
      return message;
      }


//---------------------------
// checkArray()
//---------------------------

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static void checkArray(boolean[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static void checkArray(byte[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static void checkArray(char[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static void checkArray(short[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static void checkArray(int[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static void checkArray(long[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static void checkArray(float[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static void checkArray(double[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }

   /**
    * Throws an exception is the region {@code [off, off + len]} is not
    * within the array bounds
    * @param <E>
    * @param   array   the array.
    * @param   off   the begin offset in the array.
    * @param   len   the length of the interval
    * @see  #checkNull(Object)
    * @see  #checkOffLen(int, int, int)
    */
   public static <E> void checkArray(E[] array, int off, int len) {
      Arrays.checkNull(array);
      Arrays.checkOffLen(array.length, off, len);
      }


//---------------------------
// memset()
//---------------------------

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(boolean[], boolean, int, int)
    */
   public static void memset(boolean[] array, boolean value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(byte[], byte, int, int)
    */
   public static void memset(byte[] array, byte value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(char[], char, int, int)
    */
   public static void memset(char[] array, char value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(short[], short, int, int)
    */
   public static void memset(short[] array, short value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(int[], int, int, int)
    */
   public static void memset(int[] array, int value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(long[], long, int, int)
    */
   public static void memset(long[] array, long value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(float[], float, int, int)
    */
   public static void memset(float[] array, float value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(double[], double, int, int)
    */
   public static void memset(double[] array, double value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @see #memset(Object[], Object, int, int)
    */
   public static void memset(Object[] array, Object value) {
      Arrays.memset(array, value, 0, array.length);
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @param   offset   the offset in the array.
    * @param   len   the length of the interval.
    * @see #memset(int[], int, int, int)
    */
   public static void memset(boolean[] array, boolean value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @param   offset   the offset in the array.
    * @param   len   the length of the interval.
    * @see #memset(int[], int, int, int)
    */
   public static void memset(byte[] array, byte value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @param   offset   the offset in the array.
    * @param   len   the length of the interval.
    * @see #memset(int[], int, int, int)
    */
   public static void memset(char[] array, char value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @param   offset   the offset in the array.
    * @param   len   the length of the interval.
    * @see #memset(int[], int, int, int)
    */
   public static void memset(short[] array, short value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }

   /**
    * Similar to the C function {@code <a target="_blank" href="http://www.dinkumware.com/htm_cl/string.html#memset">memset</a>}.
    * Adaption from <a target="_blank" href="http://javafaq.com/cjlfara.html">the Java FAQ</a>.
    * @param   array   the array to set each element at {@code value}.
    * @param   value   the value to set each element of {@code array}.
    * @param   offset  the index of the first element of the interval.
    * @param   len     the length of the interval.
    */
   public static void memset(int[] array, int value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @param   offset   the offset in the array.
    * @param   len   the length of the interval.
    * @see #memset(int[], int, int, int)
    */
   public static void memset(long[] array, long value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @param   offset   the offset in the array.
    * @param   len   the length of the interval.
    * @see #memset(int[], int, int, int)
    */
   public static void memset(float[] array, float value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @param   offset   the offset in the array.
    * @param   len   the length of the interval.
    * @see #memset(int[], int, int, int)
    */
   public static void memset(double[] array, double value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }

   /**
    * Sets each element of the array to the specified value.
    * @param   array   the array.
    * @param   value   the value.
    * @param   offset   the offset in the array.
    * @param   len   the length of the interval.
    * @param   <E>   the type of the elements in the array.
    * @see #memset(int[], int, int, int)
    */
   public static <E> void memset(E[] array, E value, int offset, int len) {
      Arrays.checkArray(array, offset, len);
      if (len > 0) {
         array[offset] = value;
         }
      for (int i = 1; i < len; i += i) {
         System.arraycopy(array, offset, array, i + offset, ((len - i) < i) ? (len - i) : i);
         }
      }


//---------------------------
// concat()
//---------------------------

   /**
    * @see  #concat(boolean[], int, int, boolean[], int, int, boolean[], int)
    */
   @SuppressWarnings("javadoc")
   public static boolean[] concat(boolean[] z1, boolean[] z2) {
      boolean[] array =  Arrays.concat(z1, z2, null);
      return array;
      }

   /**
    * @see  #concat(boolean[], int, int, boolean[], int, int, boolean[], int)
    */
   @SuppressWarnings("javadoc")
   public static boolean[] concat(boolean[] z1, boolean[] z2, boolean[] dest) {
      boolean[] array =  Arrays.concat(z1, 0, z1.length, z2, 0, z2.length, dest, 0);
      return array;
      }

   /**
    * @see  #concat(byte[], int, int, byte[], int, int, byte[], int)
    */
   @SuppressWarnings("javadoc")
   public static byte[] concat(byte[] b1, byte[] b2) {
      byte[] array =  Arrays.concat(b1, b2, null);
      return array;
      }

   /**
    * @see  #concat(byte[], int, int, byte[], int, int, byte[], int)
    */
   @SuppressWarnings("javadoc")
   public static byte[] concat(byte[] b1, byte[] b2, byte[] dest) {
      byte[] array =  Arrays.concat(b1, 0, b1.length, b2, 0, b2.length, dest, 0);
      return array;
      }

   /**
    * @see  #concat(short[], int, int, short[], int, int, short[], int)
    */
   @SuppressWarnings("javadoc")
   public static short[] concat(short[] s1, short[] s2) {
      short[] array = Arrays.concat(s1, s2, null);
      return array;
      }

   /**
    * @see  #concat(short[], int, int, short[], int, int, short[], int)
    */
   @SuppressWarnings("javadoc")
   public static short[] concat(short[] s1, short[] s2, short[] dest) {
      short[] array = Arrays.concat(s1, 0, s1.length, s2, 0, s2.length, dest, 0);
      return array;
      }

   /**
    * @see  #concat(char[], int, int, char[], int, int, char[], int)
    */
   @SuppressWarnings("javadoc")
   public static char[] concat(char[] c1, char[] c2) {
      char[] array = Arrays.concat(c1, c2, null);
      return array;
      }

   /**
    * @see  #concat(char[], int, int, char[], int, int, char[], int)
    */
   @SuppressWarnings("javadoc")
   public static char[] concat(char[] c1, char[] c2, char[] dest) {
      char[] array = Arrays.concat(c1, 0, c1.length, c2, 0, c2.length, dest, 0);
      return array;
      }

   /**
    * @see  #concat(int[], int, int, int[], int, int, int[], int)
    */
   @SuppressWarnings("javadoc")
   public static int[] concat(int[] i1, int[] i2) {
      int[] array = Arrays.concat(i1, i2, null);
      return array;
      }

   /**
    * @see  #concat(int[], int, int, int[], int, int, int[], int)
    */
   @SuppressWarnings("javadoc")
   public static int[] concat(int[] i1, int[] i2, int[] dest) {
      int[] array = Arrays.concat(i1, 0, i1.length, i2, 0, i2.length, dest, 0);
      return array;
      }

   /**
    * @see  #concat(long[], int, int, long[], int, int, long[], int)
    */
   @SuppressWarnings("javadoc")
   public static long[] concat(long[] l1, long[] l2) {
      long[] array = Arrays.concat(l1, l2, null);
      return array;
      }

   /**
    * @see  #concat(long[], int, int, long[], int, int, long[], int)
    */
   @SuppressWarnings("javadoc")
   public static long[] concat(long[] l1, long[] l2, long[] dest) {
      long[] array = Arrays.concat(l1, 0, l1.length, l2, 0, l2.length, dest, 0);
      return array;
      }

   /**
    * @see  #concat(float[], int, int, float[], int, int, float[], int)
    */
   @SuppressWarnings("javadoc")
   public static float[] concat(float[] f1, float[] f2) {
      float[] array = Arrays.concat(f1, f2, null);
      return array;
      }

   /**
    * @see  #concat(float[], int, int, float[], int, int, float[], int)
    */
   @SuppressWarnings("javadoc")
   public static float[] concat(float[] f1, float[] f2, float[] dest) {
      float[] array = Arrays.concat(f1, 0, f1.length, f2, 0, f2.length, dest, 0);
      return array;
      }

   /**
    * @see  #concat(double[], int, int, double[], int, int, double[], int)
    */
   @SuppressWarnings("javadoc")
   public static double[] concat(double[] d1, double[] d2) {
      double[] array = Arrays.concat(d1, d2, null);
      return array;
      }

   /**
    * @see  #concat(double[], int, int, double[], int, int, double[], int)
    */
   @SuppressWarnings("javadoc")
   public static double[] concat(double[] d1, double[] d2, double[] dest) {
      double[] array = Arrays.concat(d1, 0, d1.length, d2, 0, d2.length, dest, 0);
      return array;
      }

   /**
    * @see  #concat(Object[], int, int, Object[], int, int, Object[], int)
    */
   @SuppressWarnings("javadoc")
   public static Object[] concat(Object[] o1, Object[] o2) {
      Object[] array = Arrays.concat(o1, o2, null);
      return array;
      }

   /**
    * @see  #concat(Object[], int, int, Object[], int, int, Object[], int)
    */
   @SuppressWarnings("javadoc")
   public static Object[] concat(Object[] o1, Object[] o2, Object[] dest) {
      Object[] array = Arrays.concat(o1, 0, o1.length, o2, 0, o2.length, dest, 0);
      return array;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  z1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  z2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static boolean[] concat(boolean[] z1, int off1, int len1, boolean[] z2, int off2, int len2, boolean[] dest, int destOff) {
      Arrays.checkArray(z1, off1, len1);
      Arrays.checkArray(z2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new boolean[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(z1, off1, dest, destOff, len1);
      System.arraycopy(z2, off2, dest, destOff + len1, len2);

      return dest;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  b1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  b2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static byte[] concat(byte[] b1, int off1, int len1, byte[] b2, int off2, int len2, byte[] dest, int destOff) {
      Arrays.checkArray(b1, off1, len1);
      Arrays.checkArray(b2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new byte[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(b1, off1, dest, destOff, len1);
      System.arraycopy(b2, off2, dest, destOff + len1, len2);

      return dest;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  s1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  s2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static short[] concat(short[] s1, int off1, int len1, short[] s2, int off2, int len2, short[] dest, int destOff) {
      Arrays.checkArray(s1, off1, len1);
      Arrays.checkArray(s2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new short[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(s1, off1, dest, destOff, len1);
      System.arraycopy(s2, off2, dest, destOff + len1, len2);

      return dest;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  c1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  c2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static char[] concat(char[] c1, int off1, int len1, char[] c2, int off2, int len2, char[] dest, int destOff) {
      Arrays.checkArray(c1, off1, len1);
      Arrays.checkArray(c2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new char[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(c1, off1, dest, destOff, len1);
      System.arraycopy(c2, off2, dest, destOff + len1, len2);

      return dest;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  i1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  i2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static int[] concat(int[] i1, int off1, int len1, int[] i2, int off2, int len2, int[] dest, int destOff) {
      Arrays.checkArray(i1, off1, len1);
      Arrays.checkArray(i2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new int[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(i1, off1, dest, destOff, len1);
      System.arraycopy(i2, off2, dest, destOff + len1, len2);

      return dest;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  l1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  l2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static long[] concat(long[] l1, int off1, int len1, long[] l2, int off2, int len2, long[] dest, int destOff) {
      Arrays.checkArray(l1, off1, len1);
      Arrays.checkArray(l2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new long[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(l1, off1, dest, destOff, len1);
      System.arraycopy(l2, off2, dest, destOff + len1, len2);

      return dest;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  f1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  f2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static float[] concat(float[] f1, int off1, int len1, float[] f2, int off2, int len2, float[] dest, int destOff) {
      Arrays.checkArray(f1, off1, len1);
      Arrays.checkArray(f2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new float[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(f1, off1, dest, destOff, len1);
      System.arraycopy(f2, off2, dest, destOff + len1, len2);

      return dest;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  d1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  d2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static double[] concat(double[] d1, int off1, int len1, double[] d2, int off2, int len2, double[] dest, int destOff) {
      Arrays.checkArray(d1, off1, len1);
      Arrays.checkArray(d2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new double[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(d1, off1, dest, destOff, len1);
      System.arraycopy(d2, off2, dest, destOff + len1, len2);

      return dest;
      }

   /**
    * Concatenates two parts of two arrays. After having checked the arrays
    * with <code>checkArray()</code>, <code>System.arraycopy()</code> is
    * performed to copy the first part, then again for the second part. If
    * <code>dest</code> is <code>null</code>, it will be created with its
    * size equal to <code>destOff + len1 + len2</code> and filled starting at
    * <code>destOff</code>.
    * @param  o1  the first array.
    * @param  off1  the first array offset
    * @param  len1  the first array number of elements to copy.
    * @param  o2  the second array.
    * @param  off2  the second array offset.
    * @param  len2  the second array number of elements to copy.
    * @param  dest  the destination array. If it is not <code>null</code>,
    *                it must have sufficient space starting at <code>destOff</code>.
    * @param  destOff  the destination array offset.
    * @return  <code>dest</code>
    */
   public static Object[] concat(Object[] o1, int off1, int len1, Object[] o2, int off2, int len2, Object[] dest, int destOff) {
      Arrays.checkArray(o1, off1, len1);
      Arrays.checkArray(o2, off2, len2);

      int destLen = len1 + len2;
      if (dest == null) {
         dest = new Object[destOff + destLen];
         }
      else {
         Arrays.checkArray(dest, destOff, destLen);
         }

      System.arraycopy(o1, off1, dest, destOff, len1);
      System.arraycopy(o2, off2, dest, destOff + len1, len2);

      return dest;
      }

   }
